package org.wsff.tools.api.gateway.core.monitor;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.wsff.tools.api.gateway.error.GatewayException;
import org.wsff.tools.api.gateway.manager.Lifecycle;
import org.wsff.tools.api.gateway.model.BaseRequest;
import org.wsff.tools.api.gateway.model.BaseResponse;

/**
 * 网关监控器抽象类
 *
 * @author ryan
 * @version Id: AbstractGatewayMonitor.java, v 0.1 2022-02-07 18:08 ryan Exp $$
 */
@Slf4j
public abstract class AbstractGatewayMonitor implements GatewayMonitor, Lifecycle {

    /**
     * The statistics delay queue
     */
    private ArrayBlockingQueue<Statistics> queue;

    /**
     * The scheduled future
     */
    private ScheduledFuture<?>             scheduledFuture;

    /**
     * The timer for sending statistics
     */
    private ScheduledExecutorService       scheduledExecutorService;

    /**
     * monitor interval
     */
    @Getter
    @Setter
    private long                           monitorInterval = 60000;

    /**
     * init delay time
     */
    @Getter
    @Setter
    private long                           initialDelay    = 60000;

    /**
     * delay queue capacity
     */
    @Getter
    @Setter
    private int                            queueCapacity   = 1000;

    /**
     * 记录监控数据
     * 
     * @param statistics 记录
     */
    protected abstract void record(Statistics statistics);

    /**
     * 参数校验方法
     *
     * @param context 网关上下文
     */
    @Override
    public void monitor(MonitorGatewayContext context) {
        Statistics statistics = buildStatistics(context);
        queue.offer(statistics);
    }

    /**
     * 构建统计信息
     *
     * @param context 上下文
     * @return 统计信息
     */
    protected Statistics buildStatistics(MonitorGatewayContext context) {
        Statistics statistics = new Statistics();
        statistics.setService(context.getName());
        statistics.setReqTime(context.reqTime());
        statistics.setRespTime(context.respTime());

        Object request = context.getRequest();
        if (request instanceof BaseRequest) {
            statistics.setTracerId(((BaseRequest) request).getTracerId());
        }
        Object response = context.getResponse();
        if (response != null) {
            if (response instanceof BaseResponse) {
                BaseResponse responseDTO = (BaseResponse) response;
                statistics.setCode(responseDTO.getCode());
                statistics.setMessage(responseDTO.getMessage());
                statistics.setSuccess(responseDTO.getSuccess());
                statistics.setTracerId(StringUtils.defaultIfBlank(responseDTO.getTracerId(), statistics.getTracerId()));
            }
        } else {
            statistics.setCode(context.code());
            statistics.setMessage(context.message());
            statistics.setSuccess(false);
        }

        return statistics;
    }

    /**
     * 开始
     *
     * @throws GatewayException 异常
     */
    @Override
    public void start() throws GatewayException {
        queue = new ArrayBlockingQueue<>(1000);
        scheduledExecutorService = new ScheduledThreadPoolExecutor(3, new NamedThreadFactory(NamedThreadFactory.Builder.builder().name("GatewayMonitor")));
        scheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
            Statistics statistics = queue.poll();
            record(statistics);
        }, monitorInterval, monitorInterval, TimeUnit.MILLISECONDS);
    }

    /**
     * 销毁
     *
     * @throws GatewayException 异常
     */
    @Override
    public void destroy() throws GatewayException {
        cancelFuture();
    }

    /**
     * cancel future
     */
    private void cancelFuture() {
        try {
            ScheduledFuture<?> future = scheduledFuture;
            if (future != null && !future.isCancelled()) {
                future.cancel(true);
            }
        } catch (Throwable e) {
            log.error("Unexpected error occur at cancel sender timer, cause: " + e.getMessage(), e);
        }
    }
}
